const sha256 = require('js-sha256').sha256
import {
  JSEncrypt
} from "jsencrypt"; //引用jsencrypt
// var JSEncrypt = require('jsencrypt')
const random = require('string-random')
var CryptoJS = require("crypto-js");
const {
  util
} = require("./util.js")
const app = getApp()
const blockchain = require('./blockchain.js');
const crypto = require("./sm2Crypto.js").sm2
const _ = require("underscore")

class YlzLink {
  constructor() {

  }

  // 将数据存储至 IPFS,同时将 cid 加密
  async pushDataToIPFS(data, cidCrypt = true, publicKey = null) {

    if (!publicKey) {
      publicKey = app.globalData.publicKey;
    }

    let cid = await this.ipfsDagPut(data);

    //2、利用公钥对 cid 加密
    console.log("setData:cid", cid)
    if (cidCrypt) {
      cid = crypto.encrypt(cid, publicKey);
      console.log("setData:encryptCid", cid, publicKey)
    }

    return cid;
  }

  // 解密 cid,并且返回对应的数据
  async getDataFromCryptoCid(cid) {
    try {
      cid = crypto.decrypt(cid, app.globalData.privateKey);
    } catch (e) {
      console.log("error", e)
    }

    return await this.ipfsDagGet(cid);
  }

  async setData(data, date, dataCrypt = false, cidCrypt = true, bc = true, publicKey = null) {
    console.log("===========开始设置数据=============")
    let cid, cryptCid, resultId, hash
    //1、如果data是基本数据类型，统一转换为JSON后加入ipfs；如果是文件名则读取后转base64后加入ipfs。返回ipfs的cid

    let temp = JSON.stringify(data)

    data = JSON.parse(temp)
    hash = sha256(temp)

    console.log("setData:data", data, date, hash)

    if (dataCrypt) {
      if (publicKey) {
        data = crypto.encrypt(JSON.stringify(data), publicKey)
      } else {
        data = crypto.encrypt(JSON.stringify(data), app.globalData.publicKey)
      }
    }

    cid = await this.ipfsDagPut(data)

    //2、利用私钥对cid加密
    console.log("setData:cid", cid)
    if (cidCrypt) {
      if (publicKey) {
        cid = crypto.encrypt(cid, publicKey)
      } else {
        try {
          cid = crypto.encrypt(cid, app.globalData.publicKey)
        } catch (e) {
          console.log("error", e, app.globalData.publicKey, cid)
        }
      }
      console.log("setData:encryptCid", cid, app.globalData.publicKey)
    }
    if (bc) {
      //3.1、如果存入区块链：将加密结果用私钥签名并入链,将签名结果签名的数据入链

      // 存储 cid 的智能合约
      const contractName = 'cloudhealth5';
      const actionName = 'save';

      const accountName = await util.getStorage("blockchainAccount");

      const data = {
        account: accountName,
        date: date,
        cid: cid,
        hash: hash
      };

      resultId = await this.bcSet(contractName, actionName, data)
    } else {
      //3.2、如果存入数据库：将机密结果用私钥签名后存入数据库
      resultId = await this.dbSave(cid, date, hash)
    }
    console.log("setData:resultId", resultId)

    return resultId
  }

  async getData(arg, dataCrypt = false, cidCrypt = true, bc = true) {
    console.log("===========开始读取数据=============")
    console.log("getData:id,date,path", arg.id, arg.date, arg.path)
    let data = {},
      cid
    if (bc) {
      if (arg.id) {
        let temp = await this.bcGetByCid(arg.id)
        console.log("from blockchain1", temp)
        data = temp.data.data[temp.data.data.length - 1].act.data.cid //temp.data.data.act.data.cid
      } else {
        let accountName = await util.getStorage("blockchainAccount")
        console.log("getData:accountName", accountName)
        let temp = await this.bcGetByAccountDate(accountName, arg.date)

        temp = temp.data.data

        console.log("from blockchain2", temp)

        if (temp.length > 0) {
          data = temp[temp.length - 1].act.data.cid
        } else {
          return;
        }
      }
    } else {
      let temp = await this.dbFetch(arg.id)
      data = temp.cid
    }
    console.log("getData:Ecid/cid", data)
    let privateKey = app.globalData.privateKey
    if (cidCrypt) {
      try {

        cid = crypto.decrypt(data, privateKey)
      } catch (e) {
        console.log("error", e, privateKey)
      }
    } else {
      cid = data
    }
    console.log("getData:cid", cid)

    let result = await this.ipfsDagGet(cid, arg.path)
    if (dataCrypt) {
      result = JSON.parse(crypto.decrypt(result), privateKey)
    }
    console.log("getData:data", result)
    return result
  }

  async getLastData(arg, dataCrypt = false, cidCrypt = true, bc = true) {

    console.log("===========开始读取数据=============")
    console.log("getData:id,date,path", arg.id, arg.date, arg.path)
    let data = {},
      cid
    if (bc) {
      if (arg.id) {
        let temp = await this.bcGetByCid(arg.id)
        console.log("from blockchain1", temp)
        data = temp.data.data[temp.data.data.length - 1].act.data.cid //temp.data.data.act.data.cid
      } else {
        let accountName = await util.getStorage("blockchainAccount")
        console.log("getData:accountName", accountName)
        let temp = await this.bcGetLastByAccount(accountName)

        temp = temp.data.data

        console.log("from blockchain2", temp)

        if (temp) {
          data = temp.act.data.cid
        } else {
          return;
        }
      }
    } else {
      let temp = await this.dbFetch(arg.id)
      data = temp.cid
    }

    console.log("getData:Ecid/cid", data)
    if (cidCrypt) {
      try {
        cid = crypto.decrypt(data, app.globalData.privateKey)
      } catch (e) {
        console.log("error", e, app.globalData.privateKey)
      }
    } else {
      cid = data
    }

    console.log("getData:cid", cid)

    let result = await this.ipfsDagGet(cid, arg.path)
    if (dataCrypt) {
      result = JSON.parse(crypto.decrypt(result), app.globalData.privatKey)
    }
    console.log("getData:data", result)
    return result
  }

  async getRangeData(arg, dataCrypt = false, cidCrypt = true, bc = true) {
    console.log("===========开始读取数据=============")
    console.log("getData:id,sdate,edate,path", arg.id, arg.sdate, arg.edate, arg.path)
    let data = [],
      cid
    if (bc) {
      if (arg.id) {
        let temp = await this.bcGetByCid(arg.id)
        console.log("from blockchain1", temp)
        data.push(temp.data.data.act.data.cid)
      } else {
        let accountName = await util.getStorage("blockchainAccount")
        console.log("getData:accountName", accountName)
        let temp = await this.bcGetByAccountRange(accountName, arg.sdate, arg.edate)
        temp = temp.data.data

        console.log("from blockchain3", temp)

        if (temp.length > 0) {
          temp.forEach(element => {
            data.push(element.act.data.cid)
          });
        }
      }
    } else {
      let temp = await this.dbFetch(arg.id)
      data = temp.cid
    }
    console.log("getData:Ecid/cids", data)

    let cids = []
    if (cidCrypt) {
      cids = data.map(cid => {
        try {
          return crypto.decrypt(cid, app.globalData.privateKey)
        } catch (e) {
          console.log("error", e, app.globalData.privateKey)
        }
      })
    } else {
      cid = data
    }
    console.log("getData:cid", cids)

    let result = []
    result = await Promise.all(cids.map(async ipfsid => {
      const letresult = await this.ipfsDagGet(ipfsid, arg.path)
      let ipfsresult = letresult
      if (dataCrypt) {
        ipfsresult = JSON.parse(crypto.decrypt(letresult), app.globalData.privatKey)
      }
      console.log("getData:data", ipfsresult)
      return ipfsresult
    }))
    return result
  }

  async ipfsAdd(data) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/ipfs/add`,
      method: "post",
      data: {
        data
      }
    })
    let cids = result.data
    return cids
  }

  async ipfsCat(cid, path = "") {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/ipfs/cat`,
      method: "post",
      data: {
        cid,
        path
      }
    })
    let data = result.data
    return data
  }

  async ipfsDagPut(data, getAll = false) {
    console.log('****' + sha256(JSON.stringify(data)))
    let result = await util.request({
      url: `${app.globalData.serverAddress}/ipfs/dagPut`,
      method: "post",
      data: {
        data,
        getAll
      }
    })
    let cid = result.data
    console.log('****' + sha256(JSON.stringify(cid)))
    return cid
  }
  async ipfsDagGet(cid, path) {

    let result = await util.request({
      url: `${app.globalData.serverAddress}/ipfs/dagGet`,
      method: "post",
      data: {
        cid: cid,
        path: path
      }
    })
    return result.data
  }
  async ipfsUpload() {

  }
  async ipfsDownload() {

  }
  async ipfsKeyGen(name) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/ipfs/keyGen/${name}`,
      method: "get",
    })
    return result.data
  }
  async ipfsNamePublish(cid, lifetime, key) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/ipfs/namePublish/${cid}`,
      method: "post",
      data: {
        lifetime,
        key
      }
    })
    return result.data
  }
  /*
   * @api 将数据存入区块链
   * @apiParam {String} contractName 智能合约名称
   * @apiParam {String} actionName  智能合约方法名
   * @apiParam {String} data  业务数据
   */
  async bcSet(contractName, actionName, data) {
    const accountName = await util.getStorage("blockchainAccount");

    const actions = [{
      account: contractName,
      name: actionName,
      authorization: [{
        actor: accountName,
        permission: 'active'
      }],
      data: data
    }];

    return await blockchain.blockchainSet(actions);
  }
  /*
   * @api 根据账户名查询对应的区块链数据
   * @apiParam {String} accountName 对应的区块链账户名
   */
  async bcGetByAccount(accountName) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/v1/service/get_record_by_account?account=` + accountName,
      method: "get"
    });
    return result;

  }
  /*
   * @api 根据 cid 查询对应的区块链数据
   * @apiParam {String} cid 存入的 cid
   */
  async bcGetByCid(cid) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/v1/service/get_record_by_cid?cid=` + cid,
      method: "get"
    });
    return result;
  }
  /*
   * @api 根据账户名和日期查询对应的区块链数据
   * @apiParam {String} accountName 对应的区块链账户名
   * @apiParam {String} date 对应的日期
   */
  async bcGetByAccountDate(accountName, date) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/v1/service/get_record_by_date?account=${accountName}&date=${date}`,
      method: "get"
    });
    return result;
  }
  /*
   * @api 根据账户名和日期查询对应的区块链数据
   * @apiParam {String} accountName 对应的区块链账户名
   * @apiParam {String} date 对应的日期
   */
  async bcGetLastByAccountDate(accountName, date) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/v1/service/get_latest_record?account=${accountName}&date=${date}`,
      method: "get"
    });
    return result;
  }
  /*
   * @api 根据账户名和日期查询对应的区块链数据
   * @apiParam {String} accountName 对应的区块链账户名
   * @apiParam {String} date 对应的日期
   */
  async bcGetLastByAccount(accountName) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/v1/service/get_latest_record?account=${accountName}`,
      method: "get"
    });

    return result;
  }
  /*
   * @api 根据账户名和日期范围查询对应的区块链数据
   * @apiParam {String} accountName 对应的区块链账户名
   * @apiParam {String} date 对应的日期范围
   */
  async bcGetByAccountRange(accountName, sdate, edate) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/v1/service/get_record_by_range?account=${accountName}&start_date=${sdate}&end_date=${edate}`,
      method: "get"
    });
    return result;
  }
  async dbSave(cid, date, hash) {
    const accountName = await util.getStorage("blockchainAccount");
    let sign = crypto.sign(cid, app.globalData.privatKey)
    let result = await util.request({
      url: `${app.globalData.serverAddress}/db/save`,
      method: "post",
      data: {
        data: {
          address: accountName,
          cid: cid,
          date: date,
          hash: hash,
          publicKey: app.globalData.publicKey,
          sign: sign,
          timestamp: new Date()
        }
      }
    })
    return result.data.insertedId
  }
  async dbFetch(dbid) {
    let result = await util.request({
      url: `${app.globalData.serverAddress}/db/fetch/${dbid}`,
      method: "get"
    })
    return result.data
  }

  //
  async save2bc(accountName, date, cid, hash, prehash, tag) {
    // 存储 cid 的智能合约
    const contractName = 'cloudhealth5';
    const actionName = 'save';
    const data = {
      account: accountName,
      date,
      cid,
      hash,
      prehash,
      tag //用于分享时区分
    };

    const resultId = await this.bcSet(contractName, actionName, data)
    return resultId
  }
  /**
   * 处理小程序中待入链待数据
   * @param {*} datas //storage中data部分
   */


  async datas2ipfs(jsondatas) {
    const accountName = await util.getStorage("blockchainAccount");
    // //按日期分组
    // const jsondatas = this.YlzData.list2jsonv2(datas)  
    let bcresultids = []
    for (let i in jsondatas) {
      //查找链上数据
      const bcldata = await this.bcGetLastByAccountDate(accountName, i)

      const bcdata = bcldata.data.data
      console.log('链上数据' + JSON.stringify(bcdata))
      console.log('查找链上数据参数a' + accountName + '查找链上数据参数b' + i)


      // console.log('bcchandu' + bcdata.length)
      if (!bcdata) {
        console.log('....a')
        //bc上没有对于数据可视为新添数据直接入链
        //入链前查看是否有身高，体重等持续性summary，如果没有可以往前取，保持结构完整
        if (jsondatas[i].summary['sign.H'] == 0 || jsondatas[i].summary['sign.W'] == 0) {
          const pre = await this.getLastData({}, false, true, true)
          if (pre) {
            const predate = Object.keys(pre)[0]
            if (jsondatas[i].summary['sign.H'] == 0) {
              jsondatas[i].summary['sign.H'] = pre[predate].summary['sign.H']
            }
            if (jsondatas[i].summary['sign.W'] == 0) {
              jsondatas[i].summary['sign.W'] = pre[predate].summary['sign.W']
            }
          }
        }
        const cid = await this.setData({
          [i]: jsondatas[i]
        }, i)

        console.log('ipfsid' + cid)
        bcresultids.push(cid)
      } else {
        //对比
        //当天数据肯定是数据副本，直接入链更新副本
        //历史数据有可能是若干单条数据，但必须为修改；先取回再修改重新入链

        console.log('不分当天还是历史，统一与链上数据对比处理')
        //避免数据副本不全，跨日的只需添加修改的数据项

        let predata = await this.getData({
          id: bcdata.act.data.cid
        })
        if (!predata[i]) {
          predata = {
            [i]: predata
          }
        }
        //jsondatas[i]判断操作类型 add，delete，modify
        for (let key in jsondatas[i]) {
          if (key == 'summary') {
            predata[i][key] = jsondatas[i]['summary']
          } else {
            jsondatas[i][key].map(async keydata => {

              if (keydata.operate == 'delete') { //删

                const modindex = predata[i][key].findIndex(pkdata => pkdata.hash == keydata.hash)
                predata[i][key].splice(modindex, 1)
              } else if (keydata.operate == 'modify') { //改
                //直接插入还是更新原数据
                const modindex = predata[i][key].findIndex(pkdata => pkdata.hash == keydata.hash)
                //用原hash找到历史数据后必须更新hash
                let {
                  ...modtem
                } = keydata
                modtem['hash'] = sha256(JSON.stringify(modtem))
                predata[i][key].splice(modindex, 1, modtem)
              } else { //增加
                if (!predata[i][key]) {
                  predata[i][key] = []
                  predata[i][key].push(keydata)
                } else {
                  const addindex = predata[i][key].findIndex(pkdata => pkdata.hash == keydata.hash)
                  if (addindex < 0) {
                    predata[i][key].push(keydata)
                  }
                }
              }
            })
          }
        }
        const currenthash = sha256(JSON.stringify(predata[i]))
        if (bcdata.act.data.hash != currenthash) {
          //更新处理完后再将副本重新入链
          const cid = await this.setData({
            [i]: predata[i]
          }, i)
          console.log('ipfsid' + cid)
          bcresultids.push(cid)
        } else {
          console.log('重复数据不作处理')
        }
      }
    }
    return bcresultids
  }

  /**
   * 上云数据签名
   * @param {*} data 
   */
  async encryptValue(data) {
    const mpubkey = `-----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAonlW/XD9FdnpamwyudaT0Fv4HFyIFcikUpv+GAk8857jABnM6ZuJYn2yfx5M9k8CFLY754fW2N20GmWPHWzjhodzYBUMZvXg/thTmhWPhmL8f7YddlEhCKgRr1GFmUL9yWex1qsh4ruwscMWSceVzIZsYtix4WvVbhPoVyZJGU0zAyK8cyq2sDZv8/	ZlpQH4IgTolgfmA7DXq+LX1v9FHofZtkxj/XioVD6wfyvC8OUuLOxd3+NgRunaMO2XJAqcY+dZl7Oe6XDkL7hDebUSITFh1M+eO0MfdA+26OZzsRaoNYreu2zyCrRCYPINAg2hAAorPx/R1VMqCckEWy2EIQIDAQAB
    -----END PUBLIC KEY-----`
    const mpvkey = `-----BEGIN RSA PRIVATE KEY-----
    MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAzSKFoapAB6TPTFnv ey0tUXLHWHb9v10qVsDNCdfuC8YpOc02Ku0C4F1iZ6chvfE92DDB56cekpuiQMlx 849owQIDAQABAkEAnLmubUic6PojI0tnK63lfN/7q/wlfEpa6bMeNFIUXksk8mmn ukoa4uQELY9GkRVcuHVQLElNN1wqJDs9iKiijQIhAP4oWoJflEZnKBhmdjbayaQL JlNh5whSXcq6Ut3pOvV7AiEAzp8yGNZyfkmISKmD993iQfhYcZzTqohIgnZq3w4T n/MCIQC2mG9OMzTnz7zyqspLZqxvesMyLKg7RlG0EGDwpVuCeQIhAJ3VQaFzfrY1 WWOYCfT3i3Q1TupKj+2+eRUfe/ezew49AiAbHf5KZYLe+XMflsd2HiLiePrlmyXI /ndKXZv/r+JbSg==
    -----END RSA PRIVATE KEY-----`
    const presigndata = JSON.stringify(data)

    //1使用自己的私钥对传输数据rsa签名
    ///////////////////////
    var sign = new JSEncrypt();
    sign.setPrivateKey(mpvkey);
    var eSig = sign.sign(presigndata, CryptoJS.SHA256, "sha256");
    console.log("加签结果：" + eSig)
    //////////////////////////

    //2使用key对传输数据aes加密
    let key = random(16)

    //aes加密
    const ikey = CryptoJS.enc.Utf8.parse(key);
    const iv = CryptoJS.enc.Utf8.parse(key);


    const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), ikey, {
      iv: iv,
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    });


    // 转换为字符串
    const cipVaile = encrypted.toString();

    //3使用平台公钥对key加密
    /////////////
    // Encrypt with the public key...
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey(mpubkey);
    var eKey = encrypt.encrypt(key);
    console.log("加密结果：" + eKey)
    ///////////////////////

    return {
      params: cipVaile,
      key: eKey,
      sign: eSig
    }
  }

  /**
   * 上云数据签名
   * @param {*} data 
   */
  async decryptValue(data) {
    const sspubkey = `-----BEGIN PUBLIC KEY-----
   MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAonlW/XD9FdnpamwyudaT0Fv4HFyIFcikUpv+GAk8857jABnM6ZuJYn2yfx5M9k8CFLY754fW2N20GmWPHWzjhodzYBUMZvXg/thTmhWPhmL8f7YddlEhCKgRr1GFmUL9yWex1qsh4ruwscMWSceVzIZsYtix4WvVbhPoVyZJGU0zAyK8cyq2sDZv8/	ZlpQH4IgTolgfmA7DXq+LX1v9FHofZtkxj/XioVD6wfyvC8OUuLOxd3+NgRunaMO2XJAqcY+dZl7Oe6XDkL7hDebUSITFh1M+eO0MfdA+26OZzsRaoNYreu2zyCrRCYPINAg2hAAorPx/R1VMqCckEWy2EIQIDAQAB
   -----END PUBLIC KEY-----`
    const ssprivateKey = `-----BEGIN RSA PRIVATE KEY-----
    MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAzSKFoapAB6TPTFnv ey0tUXLHWHb9v10qVsDNCdfuC8YpOc02Ku0C4F1iZ6chvfE92DDB56cekpuiQMlx 849owQIDAQABAkEAnLmubUic6PojI0tnK63lfN/7q/wlfEpa6bMeNFIUXksk8mmn ukoa4uQELY9GkRVcuHVQLElNN1wqJDs9iKiijQIhAP4oWoJflEZnKBhmdjbayaQL JlNh5whSXcq6Ut3pOvV7AiEAzp8yGNZyfkmISKmD993iQfhYcZzTqohIgnZq3w4T n/MCIQC2mG9OMzTnz7zyqspLZqxvesMyLKg7RlG0EGDwpVuCeQIhAJ3VQaFzfrY1 WWOYCfT3i3Q1TupKj+2+eRUfe/ezew49AiAbHf5KZYLe+XMflsd2HiLiePrlmyXI /ndKXZv/r+JbSg==
    -----END RSA PRIVATE KEY-----`


    //1解密key
    //////////////////////////////////
    const preencStr = data.key
    var decrypt = new JSEncrypt();
    decrypt.setPrivateKey(ssprivateKey);
    var dkey = decrypt.decrypt(preencStr);
    console.log("解密key", dkey)

    //////////////////////////////////////////

    //2aes解密密文
    const ikey = CryptoJS.enc.Utf8.parse(dkey);
    const iv = CryptoJS.enc.Utf8.parse(dkey);
    const encrypted = CryptoJS.AES.decrypt(data.result, ikey, {
      iv: iv,
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    });

    // 转换为字符串
    const cipVaile = encrypted.toString(CryptoJS.enc.Utf8);
    const Fres = cipVaile.toString()
    let preSig = data.sign
    //3验签
    /////////////////////////////
    // Verify with the public key...
    var verify = new JSEncrypt();
    verify.setPublicKey(sspubkey);
    var ver = verify.verify(Fres, preSig, CryptoJS.SHA256);

    console.log("验签成功", ver)
    ////////////////////////////////////
    if (ver) {
      return {
        ver: true,
        data: JSON.parse(Fres)
      }
    } else {
      return {
        error: "验签失败，数据可能已经被篡改！"
      }
    }
  }

  /**
   * 签约个人健康云
   */
  async signplat(data) {
    const predata = {
      "service_id": "signing",
      "params": {
        "cert_no": data.cer_no, //测试
        "name": data.name,
        "phone": data.phone,
        "account": data.account,
        "pub_key": data.pub_key
      }
    }
    const encryptdata = await this.encryptValue(predata)

    const re = await new Promise((resolve, reject) => {
      wx.request({
        url: app.globalData.serverAddress + '/dataPlat/sign',
        // url: 'http://localhost:5000/dataPlat/sign',
        method: 'POST',
        data: encryptdata,
        header: {
          'content-type': 'application/json'
        },
        success: async function (res) {

          if (res.data.code === 200) {
            resolve(res.data.data)
          } else {
            reject(res.data.message)
          }
        },
        fail: function (err) {

          reject(err)
        }
      })
    }).catch(function (e) {
      console.log(e)
    })
    debugger
    const fres = await this.decryptValue(re)
    console.log(fres)

    return fres
  }
  /**
   * 健康数据应用
   * @param {*} data 
   */
  async getCloudData(data) {
    const encryptdata = await this.encryptValue(data)

    const re = await new Promise((resolve, reject) => {
      wx.request({
        url: app.globalData.serverAddress + '/dataPlat/getData',
        method: 'POST',
        data: encryptdata,
        header: {
          'content-type': 'application/json'
        },
        success: async function (res) {

          if (res.data.code === 200) {
            resolve(res.data.data)
          } else {
            reject(res.data.message)
          }
        },
        fail: function (err) {
          reject(err)
        }
      })
    }).catch(function (e) {
      console.log(e)
    })

    const fres = await this.decryptValue(re)
    console.log(fres)

    return fres
  }
  /**
   * 健康数据上云链
   * @param {*} data 
   */
  async data2Cloud(data) {
    const encryptdata = await this.encryptValue(data)

    const re = await new Promise((resolve, reject) => {
      wx.request({
        url: app.globalData.serverAddress + '/dataPlat/d2c',
        method: 'POST',
        data: encryptdata,
        header: {
          'content-type': 'application/json'
        },
        success: async function (res) {

          if (res.data.code === 200) {
            resolve(res.data.data)
          } else {
            reject(res.data.message)
          }
        },
        fail: function (err) {
          reject(err)
        }
      })
    }).catch(function (e) {
      console.log(e)
    })

    const fres = await this.decryptValue(re)
    console.log(fres)

    return fres
  }
  /**
   * 自研增值服务
   * @param {*} data 
   */
  async getCloudService(data) {
    const predata = {
      "service_id": "research",
      "params": {
        "user_token": data.user_token,
        "data_type": "xx",
        "rs_id": data.rs_id,
        "data_format": "json",
        "begin_date": data.begin_date,
        "end_date": data.end_date
      }
    }
    const encryptdata = await this.encryptValue(predata)
    const re = await new Promise((resolve, reject) => {
      wx.request({
        url: app.globalData.serverAddress + '/dataPlat/getService',
        // url: 'http://localhost:5000/dataPlat/sign',
        method: 'POST',
        data: encryptdata,
        header: {
          'content-type': 'application/json'
        },
        success: async function (res) {

          if (res.data.code === 200) {
            resolve(res.data.data)
          } else {
            reject(res.data.message)
          }
        },
        fail: function (err) {

          reject(err)
        }
      })
    }).catch(function (e) {
      console.log(e)
    })

    const fres = await this.decryptValue(re)
    console.log(fres)

    return fres
  }
}


///////////////////////////////////
// 因为需要小程序运行环境,暂时没办法独立成单独的测试脚本

//测试代码
/*
const test = new YlzLink();

// 创建新账户测试 需要小程序运行环境
async function testCreate(){
  try{
    const accountName = await blockchain.createBlockchainAccount();
    console.log('创建账户名成功, ' + accountName);
  }catch(error){
    console.log(error);
  }
}

// testCreate();

// 写入区块数据测试
async function testSave() {

  const accountName = await blockchain.createBlockchainAccount();

  const contractName = 'cloudhealth5';
  const actionName = 'save';
  const data = {
     account: accountName,
     date: util.formatDate(new Date()),
     cid:'cid',
     hash:sha256('Hello World.')
  };

  const transactionId = await test.bcSet(contractName,actionName,data);
  console.log('transactionId: ' + transactionId);
}

// 写入区块数据测试
testSave();

// 根据账户名查询对应的区块链数据
async function testBCGetByAccount() {
  const accountName = await util.getStorage("blockchainAccount");
  let result = test.bcGetByAccount(accountName);
  console.log(result);
}
testBCGetByAccount();

// 根据 cryptCid 查询对应的区块链数据
async function testBCGetByCid() {
  const bcGetByCid = "cid";
  let result = test.bcGetByCid(bcGetByCid);
  console.log(result);
}
testBCGetByCid();
 */
///////////////////////////////////

class YlzData {
  chain(data) {
    this.data = data
    return this
  }
  /*
   list = [{ key: "eat", value: { food: "apple", value: 1, eDate: "2020.03.04", eTime: "07:00:00" } },
   {key:"eat",value:{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"}},
   {key:"eat",value:{food:"pear",value:3,eDate:"2020.03.04",eTime:"08:00:00"}},
   {key:"eat",value:{food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}},
   {key:"weight",value:{value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}},
   {key:"weight",value:{value:75,eDate:"2020.03.05",eTime:"07:30:00"}}
   ]
   */
  list2json(key, sDate, eDate) {
    //将具备key为类别，value为具体数据，且具体数据中含有eDate,eTime的日期和时间的记录数组转换成json结构
    //json的结构将以日期为键分组，然后对每个key类别形成按时间生序的对象列表
    //例如：传入数据：
    //list = [{key:"eat",value:{food:"apple",value:1,eDate:"2020.03.04",eTime:"07:00:00"}},
    // {key:"eat",value:{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"}},
    // {key:"eat",value:{food:"pear",value:3,eDate:"2020.03.04",eTime:"08:00:00"}},
    // {key:"eat",value:{food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}},
    // {key:"weight",value:{value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}},
    // {key:"weight",value:{value:75,eDate:"2020.03.05",eTime:"07:30:00"}}
    // ]
    // 输出数据:
    // {
    // "2020.03.04":{"eat":[{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"},
    // { food: "apple", value: 1, eDate: "2020.03.04", eTime: "07:00:00" },
    // { food: "pear", value: 3, eDate: "2020.03.04", eTime: "08:00:00" }]},
    // "2020.03.05":{"eat":[{food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}],
    // "weight":[{value:75,eDate:"2020.03.05",eTime:"07:30:00"},
    // {value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}]
    // }
    try {
      let res1, res2, res3
      res1 = _.chain(this.data).filter(x => x.key == key || !key).filter(x => (x.value.eDate >= sDate || !sDate) && (x.value.eDate <= eDate || !eDate))
      res2 = res1.groupBy(x => x.value.eDate).mapObject(x => _.sortBy(x, y => y.value.eTime))
      res3 = res2.mapObject(x => {
        return _.chain(x).groupBy(y => y.key).mapObject(y => _.map(y, z => z.value)).value()
      }).value()
      this.data = res3
      return this
    } catch (err) {
      console.log("!!!!!", "list2json函数出错", list, err)
    }
  }
  //
  list2jsonv2(list) {

    //将具备key为类别，value为具体数据，且具体数据中含有eDate,eTime的日期和时间的记录数组转换成json结构
    //json的结构将以日期为键分组，然后对每个key类别形成按时间生序的对象列表
    //例如：传入数据：
    //list = [{key:"eat",value:{food:"apple",value:1,eDate:"2020.03.04",eTime:"07:00:00"}},
    // {key:"eat",value:{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"}},
    // {key:"eat",value:{food:"pear",value:3,eDate:"2020.03.04",eTime:"08:00:00"}},
    // {key:"eat",value:{food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}},
    // {key:"weight",value:{value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}},
    // {key:"weight",value:{value:75,eDate:"2020.03.05",eTime:"07:30:00"}}
    // ]
    // 输出数据:
    // {
    // "2020.03.04":{"eat":[{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"},
    // { food: "apple", value: 1, eDate: "2020.03.04", eTime: "07:00:00" },
    // { food: "pear", value: 3, eDate: "2020.03.04", eTime: "08:00:00" }]},
    // "2020.03.05":{"eat":[{food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}],
    // "weight":[{value:75,eDate:"2020.03.05",eTime:"07:30:00"},
    // {value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}]
    // }
    try {
      let res = {}
      list.map(data => {
        let weightcount = 0
        let weighttotal = 0
        let temcount = 0
        let temtotal = 0
        let hcount = 0
        let htotal = 0


        if (!res[data.value.eDate]) {
          res[data.value.eDate] = {}
        }
        if (!res[data.value.eDate]) {
          res[data.value.eDate] = {}
        }
        if (!res[data.value.eDate]['summary']) {
          res[data.value.eDate]['summary'] = {
            nutrition: {
              energy: 0,
              protein: 0,
              fat: 0,
              CHO: 0,
              Na: 0
            },
            'behavior.sleep': 0,
            'behavior.buy': 0,
            'sign.W': 0,
            'sign.T': 0,
            'sign.H': 0,
            date: data.value.eDate
          }
        }

        if (!res[data.value.eDate][data.key]) {
          res[data.value.eDate][data.key] = []
        }

        switch (data.key) {
          case 'sign.W':
            weightcount += 1
            weighttotal += data.value.value
            res[data.value.eDate]['summary']['sign.W'] = parseFloat((weighttotal / weightcount).toFixed(2))
            break
          case 'diet.food':
            res[data.value.eDate]['summary']['nutrition']['energy'] += data.value.nutrition.energy ? data.value.nutrition.energy[0] : 0
            res[data.value.eDate]['summary']['nutrition']['protein'] += data.value.nutrition.protein ? data.value.nutrition.protein[0] : 0
            res[data.value.eDate]['summary']['nutrition']['fat'] += data.value.nutrition.fat ? data.value.nutrition.fat[0] : 0
            res[data.value.eDate]['summary']['nutrition']['CHO'] += data.value.nutrition.CHO ? data.value.nutrition.CHO[0] : 0
            res[data.value.eDate]['summary']['nutrition']['Na'] += data.value.nutrition.Na ? data.value.nutrition.Na[0] : 0
            break
          case 'sign.T':
            temcount += 1
            temtotal += data.value.value
            res[data.value.eDate]['summary']['sign.T'] = parseFloat((temtotal / temcount).toFixed(2))
            break
          case 'behavior.sleep':
            res[data.value.eDate]['summary']['behavior.sleep'] += data.value.value ? data.value.value : 0
            break
          case 'behavior.buy':
            res[data.value.eDate]['summary']['behavior.buy'] += data.value.spend ? data.value.spend : 0
            break
          default:
            hcount += 1
            htotal += data.value.value
            res[data.value.eDate]['summary']['sign.H'] = parseFloat((htotal / hcount).toFixed(2))
        }
        res[data.value.eDate][data.key].push(data.value)
      })
      console.log(res)
      return res
    } catch (err) {
      console.log("!!!!!", "list2json函数出错", list, err)
    }
  }
  //
  list2rangeJson(key, sDate, eDate) {
    //将具备key为类别，value为具体数据，且具体数据中含有eDate,eTime的日期和时间的记录数组转换成json结构
    //json的结构将以日期为键分组，然后对每个key类别形成按时间生序的对象列表
    //例如：传入数据：
    //list = [{key:"eat",value:{food:"apple",value:1,eDate:"2020.03.04",eTime:"07:00:00"}},
    // {key:"eat",value:{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"}},
    // {key:"eat",value:{food:"pear",value:3,eDate:"2020.03.04",eTime:"08:00:00"}},
    // {key:"eat",value:{food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}},
    // {key:"weight",value:{value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}},
    // {key:"weight",value:{value:75,eDate:"2020.03.05",eTime:"07:30:00"}}
    // ]
    // 输出数据为:
    // {
    //   start:"2020.03.04",end:"2020.03.05",
    //     value:{"eat":[{food:"banana",value:2,eDate:"2020.03.04",eTime:"06:00:00"},
    //                   {food: "apple", value: 1, eDate: "2020.03.04", eTime: "07:00:00" },
    //                   {food: "pear", value: 3, eDate: "2020.03.04", eTime: "08:00:00" },
    //                   {food:"mongo",value:1,eDate:"2020.03.05",eTime:"07:00:00"}],
    //            "weight":[{value:75,eDate:"2020.03.05",eTime:"07:30:00"},
    //                      {value:77.5,eDate:"2020.03.05",eTime:"10:00:00"}]
    // }
    try {
      let res1, res2, res3
      res1 = _.chain(this.data).filter(x => x.key == key || !key).filter(x => (x.value.eDate >= sDate || !sDate) && (x.value.eDate <= eDate || !eDate))
      res2 = res1.groupBy(x => x.key).mapObject(x => _.sortBy(x, y => y.value.eDate + y.value.eTime))
      res3 = res2.mapObject(x => x.map(y => y.value)).value()
      this.data = {
        start: sDate,
        end: eDate,
        value: res3
      }
      return this
    } catch (err) {
      console.log("!!!!!", "list2rangeJson函数出错", list, err)
    }
  }


  /*针对list2json后的数据，选取特定键的数据。例如以下选取2020.03.04日期下eat的第一条数据
  filter("2020.03.04/eat/0")
  */
  filter(path) {
    if (!Array.isArray(path))
      path = path.split("/")
    var data
    for (let key of path) {
      data = data[key]
    }
    this.data = data
    return this
  }
  /*
  针对当前的数组数据，选取特定的键列表的数据。例如以下选取value,nutrition/fat/0,etime三列数据
    filelds(["value","nutrition/fat/0","etime"])
  */
  fields(keyArray) {
    var data = this.data
    this.data = data.map(x => {
      var y = []
      for (let key of keyArray) {
        var x1 = x
        if (!Array.isArray(key))
          key = key.split("/")
        for (let item of key) {
          x1 = x1[item]
        }
        y.push(x1)
      }
      return y
    })
    return this
  }
  sum() {
    return this.data.reduce((x, y) => parseFloat(x) + parseFloat(y))
  }
  cnt() {
    return this.data.length
  }
  avg() {
    let cnt = this.data.length;
    return (this.sum() / cnt).toFixed(2)
  }
  max() {
    return this.data.reduce((x, y) => x > y ? x : y)
  }
  min() {
    return this.data.reduce((x, y) => x > y ? y : x)
  }
}

module.exports = {
  ylzLink: new YlzLink(),
  ylzData: new YlzData()
};